/**
 * unit，该工具封装了对 zip 压缩包文件的创建和解压工具方法
 * 创建者：Bryce
 * 创建时间：20211130
 */
package file

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
)

/**
 * Zip，压缩文件或目录为 zip 文件
 * Params：
 *   source：要压缩的文件或文件夹, 绝对路径和相对路径都可以
 *   target：生成的压缩文件名，如 test.zip
 *   filter：要过滤的文件正则表达式，(采用 Golang 的 包 path.Match 进行过滤)，不过滤则传入空字符串
 * Return：
 *   操作错误信息
 */
func Zip(source, target, filter string) error {
	// 将传入路径直接转化为绝对路径
	var err error
	if isAbs := filepath.IsAbs(source); !isAbs {
		source, err = filepath.Abs(source)
		if err != nil {
			return err
		}
	}
	// 创建 zip 包文件
	zipFile, err := os.Create(target)
	if err != nil {
		return err
	}
	defer func() {
		if err := zipFile.Close(); err != nil {
			fmt.Println("File close error", err.Error(), zipFile.Name())
		}
	}()
	// 创建zip.Writer
	zw := zip.NewWriter(zipFile)
	defer func() {
		if err := zw.Close(); err != nil {
			fmt.Println("zipWriter close error", err.Error(), zipFile.Name())
		}
	}()
	// 检查资源目录或文件是否存在
	info, err := os.Stat(source)
	if err != nil {
		return err
	}
	// 如果资源是目录的话,获取根目录
	var baseDir string
	if info.IsDir() {
		baseDir = filepath.Base(source)
	}
	err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		//将遍历到的路径与 pattern 进行匹配
		ism, err := filepath.Match(filter, info.Name())
		if err != nil {
			return err
		}
		//如果匹配就忽略
		if ism {
			return nil
		}
		//创建文件头
		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}
		if baseDir != "" {
			header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
		}
		if info.IsDir() {
			header.Name += "/"
		} else {
			header.Method = zip.Deflate
		}
		//写入文件头信息
		writer, err := zw.CreateHeader(header)
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		//写入文件内容
		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer func() {
			if err := file.Close(); err != nil {
				fmt.Println("File close error", err.Error(), zipFile.Name())
			}
		}()
		_, err = io.Copy(writer, file)
		return err
	})
	if err != nil {
		return err
	}
	return nil
}

/**
 * UnZip，解压缩
 * Params：
 *   archive：要解压缩的文件，绝对路径和相对路径都可以
 *   target：解压缩后的目录名
 * Return：
 *   操作错误信息
 */
func UnZip(archive, target string) error {
	reader, err := zip.OpenReader(archive)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(target, 0755); err != nil {
		return err
	}

	for _, file := range reader.File {
		unzipPath := filepath.Join(target, file.Name)
		if file.FileInfo().IsDir() {
			err := os.MkdirAll(unzipPath, file.Mode())
			if err != nil {
				return err
			}
			continue
		}

		fileReader, err := file.Open()
		if err != nil {
			return err
		}
		defer fileReader.Close()

		targetFile, err := os.OpenFile(unzipPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}
		defer targetFile.Close()

		if _, err := io.Copy(targetFile, fileReader); err != nil {
			return err
		}
	}

	return nil
}
